package com.turbo.cloud.calc;

import com.turbo.cloud.calc.entity.RepayPlanModel;
import com.turbo.cloud.calc.finexpr.expr.Expression;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.FinanceLib;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
public class LoanCalcUtil {
    public static void main(String[] args) {
        ////BigDecimal monthRate = monthRate(new BigDecimal(6.5 / 100));
        ////System.out.printf("monthRate::" + monthRate);
        ////
        ////System.out.printf("debxOfPeriod::" + debxOfPeriod(monthRate, 24, BigDecimal.valueOf(300000), false));
        //
        ////String repayYYYYMMDD = String.format("%04d-%02d-%02d", 202, 9, 1);
        ////System.out.printf("\nrepayYYYYMMDD::%s\n", repayYYYYMMDD);
        //
        //BigDecimal monthRate = monthRate(new BigDecimal(6.5 / 100));
        //System.out.printf("monthRate::" + monthRate);
        //
        ////List<RepayPlanModel> planList = debx(monthRate, 24, 2022, 9, 15, BigDecimal.valueOf(300000), false,0);
        ////System.out.printf("debx::\n\n%s\n\n", JSONUtil.toJsonStr(planList));
        //
        //List<RepayPlanModel> planList = debj(monthRate, 19, 2022, 9, 15, BigDecimal.valueOf(300000),0);
        //System.out.printf("debj::\n\n%s\n\n", JSONUtil.toJsonStr(planList));
        //String filePath = "/Users/zhangluning/Documents/Picture/test/";
        //String filePath2 = "/Users/zhangluning/Documents/Picture/test/123.txt";
        //File file = new File(filePath);
        //try {
        //    Files.delete(Paths.get(filePath));
        //} catch (IOException e) {
        //    throw new RuntimeException(e);
        //}

        //
        //String traceId = UUID.randomUUID().toString();
        //
        //String traceIdSub = traceId.substring(0, 31);
        //String traceIdRep = traceId.replaceAll("-","");
        //
        //System.out.printf("traceIdSub:"+traceIdSub.length());
        //System.out.printf("traceIdRep:"+traceIdRep.length());


        //List<Map<String, String>> outBoundList = new ArrayList<>();
        //Map<String, String> paramsMap = new HashMap<>();
        //outBoundList.add(paramsMap);
        //
        //paramsMap.put("A","1");
        //paramsMap.put("B","2");
        //paramsMap.put("C","3");
        //
        //System.out.printf("XXX" + JSONUtil.toJsonStr(outBoundList));
    }

    /**
     * 日利率365
     *
     * @param yearRate 年利率
     */
    public static double dayRate365(BigDecimal yearRate) {
        /*
         * 公式：年利率/年天数（365）
         */
        Expression e = new Expression("yearRate/yearDay365");
        e.addVariable("yearRate", yearRate);
        e.addVariable("yearDay365", new BigDecimal(365));
        /*
         * 保留6位小数，四舍五入
         */
        return e.calculate().setScale(6, RoundingMode.UP).doubleValue();
    }

    /**
     * 日利率360
     *
     * @param yearRate 年利率
     */
    public static double dayRate360(BigDecimal yearRate) {
        /*
         * 公式：年利率/年天数（360）
         */
        Expression e = new Expression("yearRate/yearDay360");
        e.addVariable("yearRate", yearRate);
        e.addVariable("yearDay360", new BigDecimal(360));
        /*
         * 保留6位小数，四舍五入
         */
        return e.calculate().setScale(6, RoundingMode.UP).doubleValue();
    }

    /**
     * 月利率
     *
     * @param yearRate 年利率
     */
    public static BigDecimal monthRate(BigDecimal yearRate) {
        /*
         * 公式：年利率/年月份（12）
         */
        Expression e = new Expression("yearRate/yearMonth");
        e.addVariable("yearRate", yearRate);
        e.addVariable("yearMonth", new BigDecimal(12));
        /*
         * 保留6位小数，四舍五入
         */
        return e.calculate().setScale(6, RoundingMode.UP);
    }

    /**
     * 等额本息
     *
     * @param periodRate     期利率
     * @param period         期次
     * @param repayYear      还款年
     * @param repayMonth     还款月
     * @param repayDay       还款日
     * @param principalTotal 总本金
     * @param type           是否初期还款
     * @param scale          保留小数位
     */
    public static List<RepayPlanModel> debx(BigDecimal periodRate, int period, int repayYear, int repayMonth, int repayDay, BigDecimal principalTotal, boolean type, int scale) {
        List<RepayPlanModel> planList = new ArrayList<>();

        String repayYYYYMMDD = String.format("%04d-%02d-%02d 00:00:00", repayYear, repayMonth, repayDay);

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime repayDate = LocalDateTime.parse(repayYYYYMMDD, df);

        /*
         * 计算本息，使用通用的PMT公式
         * 剩余本金需要转负数
         */
        Double principalInterestTmp = FinanceLib.pmt(periodRate.doubleValue(), period, principalTotal.negate().doubleValue(), 0, type);
        BigDecimal principalInterest = BigDecimal.valueOf(principalInterestTmp).setScale(scale, RoundingMode.UP);

        /*
         * 剩余本金
         */
        BigDecimal residuePrincipal = principalTotal;

        /*
         * 循环遍历所有期次，最后一期排除掉
         */
        for (int i = 0; i < period; i++) {
            RepayPlanModel repayPlanModel = debxOfPeriod(periodRate, principalInterest, residuePrincipal, scale);
            /*
             * 期次，从0开始循环，需要加1
             */
            repayPlanModel.setTermNum(i + 1);
            /*
             * 月份累加
             */
            repayPlanModel.setShouldRepayDate(repayDate.plusMonths(i));
            /*
             * 判断是否最后一期
             */
            if (i == (period - 1)) {
                /*
                 * 最后一期,剩余所有本金赋值为末期应还本金
                 */
                repayPlanModel.setShouldRepayPrincipalAmt(residuePrincipal);
            } else {
                /*
                 * 剩余本金=总本金-每期应还本金（累加）
                 */
                residuePrincipal = residuePrincipal.subtract(repayPlanModel.getShouldRepayPrincipalAmt());
            }

            planList.add(repayPlanModel);
        }

        return planList;
    }

    /**
     * 等额本金
     *
     * @param periodRate     期利率
     * @param period         期次
     * @param repayYear      还款年
     * @param repayMonth     还款月
     * @param repayDay       还款日
     * @param principalTotal 总本金
     * @param scale          保留小数位
     */
    public static List<RepayPlanModel> debj(BigDecimal periodRate, int period, int repayYear, int repayMonth, int repayDay, BigDecimal principalTotal, int scale) {
        List<RepayPlanModel> planList = new ArrayList<>();

        String repayYYYYMMDD = String.format("%04d-%02d-%02d 00:00:00", repayYear, repayMonth, repayDay);

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime repayDate = LocalDateTime.parse(repayYYYYMMDD, df);

        /*
         * 计算本金
         * 公式：本息-利息
         */
        Expression eP = new Expression("principalTotal/period");
        eP.addVariable("principalTotal", principalTotal);
        eP.addVariable("period", BigDecimal.valueOf(period));
        BigDecimal principal = eP.calculate().setScale(scale, RoundingMode.UP);

        /*
         * 剩余本金
         */
        BigDecimal residuePrincipal = principalTotal;

        /*
         * 循环遍历所有期次，最后一期排除掉
         */
        for (int i = 0; i < period; i++) {
            RepayPlanModel repayPlanModel = debjOfPeriod(periodRate, principal, residuePrincipal, scale);
            /*
             * 期次，从0开始循环，需要加1
             */
            repayPlanModel.setTermNum(i + 1);
            /*
             * 月份累加
             */
            repayPlanModel.setShouldRepayDate(repayDate.plusMonths(i));
            /*
             * 判断是否最后一期
             */
            if (i == (period - 1)) {
                /*
                 * 最后一期,剩余所有本金赋值为末期应还本金
                 */
                repayPlanModel.setShouldRepayPrincipalAmt(residuePrincipal);
                /*
                 * 重新计算一下本息
                 * 公式：剩余本金+利息
                 */
                repayPlanModel.setShouldRepayPrinciInterAmt(residuePrincipal.add(repayPlanModel.getShouldRepayInterestAmt()));
            } else {
                /*
                 * 剩余本金=总本金-每期应还本金（累加）
                 */
                residuePrincipal = residuePrincipal.subtract(repayPlanModel.getShouldRepayPrincipalAmt());
            }

            planList.add(repayPlanModel);
        }

        return planList;
    }

    /**
     * 等额本息，计算每一期
     *
     * @param periodRate        期利率
     * @param principalInterest 本息
     * @param residuePrincipal  剩余本金
     * @param scale             小数位
     */
    private static RepayPlanModel debxOfPeriod(BigDecimal periodRate, BigDecimal principalInterest, BigDecimal residuePrincipal, int scale) {

        /*
         * 计算利息
         * 公式：期利率*剩余本金
         */
        Expression eI = new Expression("periodRate*residuePrincipal");
        eI.addVariable("periodRate", periodRate);
        eI.addVariable("residuePrincipal", residuePrincipal);
        BigDecimal interest = residuePrincipal.multiply(periodRate).setScale(scale, RoundingMode.UP);

        /*
         * 计算本金
         * 公式：本息-利息
         */
        Expression eP = new Expression("principalInterest-interest");
        eP.addVariable("principalInterest", principalInterest);
        eP.addVariable("interest", interest);
        BigDecimal principal = eP.calculate().setScale(scale, RoundingMode.UP);

        RepayPlanModel repayPlanModel = new RepayPlanModel();
        /*
         * 本金
         */
        repayPlanModel.setShouldRepayPrincipalAmt(principal);
        /*
         * 利息
         */
        repayPlanModel.setShouldRepayInterestAmt(interest);
        /*
         * 本息
         */
        repayPlanModel.setShouldRepayPrinciInterAmt(principalInterest);

        return repayPlanModel;
    }

    /**
     * 等额本金，计算每一期
     *
     * @param periodRate       期利率
     * @param principal        本金
     * @param residuePrincipal 剩余本金
     * @param scale            小数位
     */
    private static RepayPlanModel debjOfPeriod(BigDecimal periodRate, BigDecimal principal, BigDecimal residuePrincipal, int scale) {

        /*
         * 计算利息
         * 公式：期利率*剩余本金
         */
        Expression eI = new Expression("periodRate*residuePrincipal");
        eI.addVariable("periodRate", periodRate);
        eI.addVariable("residuePrincipal", residuePrincipal);
        BigDecimal interest = residuePrincipal.multiply(periodRate).setScale(scale, RoundingMode.UP);
        /*
         * 计算本息
         * 公式：本金+利息
         */
        Expression ePI = new Expression("principal+interest");
        ePI.addVariable("principal", principal);
        ePI.addVariable("interest", interest);
        BigDecimal principalInterest = ePI.calculate().setScale(scale, RoundingMode.UP);

        RepayPlanModel repayPlanModel = new RepayPlanModel();
        /*
         * 本金
         */
        repayPlanModel.setShouldRepayPrincipalAmt(principal);
        /*
         * 利息
         */
        repayPlanModel.setShouldRepayInterestAmt(interest);
        /*
         * 本息
         */
        repayPlanModel.setShouldRepayPrinciInterAmt(principalInterest);

        return repayPlanModel;
    }

}
